\chapter{A Política de Armazenamento ZooClouS}
\label{cap:politica}

O objetivo deste capítulo é apresentar o ZooClouS, a política de armazenamento
de arquivos que foi implementada no ZooNimbus. Na Seção~\ref{sec:changes} são
explicadas todas as mudanças realizadas em outros módulos de serviço do
BioNimbus. Na Seção~\ref{sec:politica} estão todas as mudanças feitas no {\it
Storage Service}, suas novas funcionalidades e como foi implementada o seu
relacionamento com o ZooKeeper e com o Avro.

\section{Mudanças nos outros Serviços do BioNimbus}
\label{sec:changes}

Com as integrações dos {\it znodes} e {\it watchers} presentes no Apache
ZooKeeper, e das chamadas RPC do Apache Avro, os módulos de serviços {\it
Monitoring Service}, {\it Discovery Service}, {\it Scheduling Service} e {\it Fault
Tolerance Service} sofreram alterações na forma em que implementavam as suas
tarefas.
Para a utilização do ZooKeeper na coordenação e controle das atividades do
ZooNimbus, foi necessária a implementação de um método responsável pelo
tratamento das notificações em todos os módulos de serviço.
Assim, todas as mudanças e atualizações que ocorrem no serviço ZooKeeper são
corretamente identificadas e utilizadas de forma personalizada em cada módulo,
modificando então a interface de implementação dos serviços, essa modificação
ocorreu baseada na interface exemplificada na
Tabela~\ref{tab:interfaceBioNimbus}, como a interface funcionava anteriormente e
na Tabela~\ref{tab:interfaceZooNimbus}, pode ser visualizado as modificações
implementadas na interface, para poder atender a questão da comunicação entre
os módulos sobre alterações e criações de atividades do ZooNimbus.

Na Tabela~\ref{tab:interfaceBioNimbus} mostra a interface de comunicação dos
serviços, na versão do BioNimbus, onde os módulos de serviços realizavam as
seguintes funções, iniciar o serviço por meio da função {\it start(P2PService
p2p)}, desligar o serviço pela função {\it shutdown()} e obter o estado do
módulo do serviço por via do método {\it getStatus()}.

\begin{table}[H]
  
  \begin{center}
  \caption{Interface Implementada pelos Serviços no BioNimbus.}
    \begin{tabular}{|l|}
	\hline
	\textbf{Service}\\ 
	\hline
	+start(P2PService p2p);\\
	+shutdown();\\
	+getStatus();\\
	\hline
    \end{tabular}

    \label{tab:interfaceBioNimbus}
  
  \end{center}

\end{table}

Para atender as novas funcionalidades implementadas na versão ZooNimbus, a
interface dos módulos de serviços recebeu novos métodos, o {\it
verifyPlugins()} e o {\it event(WatchedEvent eventType)},
métodos implementados pelos módulos de serviços para verificar
o {\it znode STATUS} de cada servidor e os {\it watchers} disparados pelo
Zookeeper, respectivamente. Como a comunicação P2P foi retirada na versão
ZooNimbus, o método {\it start()} seu parâmetro que era do tipo P2P foi
retirado. Alterações descritas na Tabela~\ref{tab:interfaceZooNimbus}.

\begin{table}[H]
  
  \begin{center}
  \caption{Nova Interface Implementada pelos Serviços no ZooNimbus.}
    \begin{tabular}{|l|}
	\hline
	\textbf{Service}\\ 
	\hline
	+start();\\
	+shutdown();\\
	+getStatus();\\
	+verifyPlugins();\\
	+event(WatchedEvent eventType);\\
	\hline
    \end{tabular}
    
    \label{tab:interfaceZooNimbus}
  
  \end{center}

\end{table}

Dessa forma, sempre que algum \textit{znode}  é alterado e existe um \textit{watcher}
responsável pelo seu monitoramento, um alerta é criado e enviado para a classe
Java que faz a implementação da Interface \textit{watcher} no projeto.
Esta classe implementada não realiza o tratamento da notificação, ela envia
essa notificação para o serviço que fez o pedido de criação do \textit{watcher}.
Ele então recebe um alerta no novo método \textit{event()} da interface
\textit{Service}, que pode ser visualizado na Tabela
\ref{tab:interfaceZooNimbus}. Assim, cada \textit{watcher} pode ser de um tipo
diferente, e essa diferença juntamente com a definição do que ela representa em
um determinado serviço é o que representa o sentido daquela notificação. Por
exemplo, no módulo de descoberta, quando um \textit{watcher} do tipo exclusão
que foi adicionado no \textit{znode} \textit{STATUS} do
\textit{peer\_IdPlugin}, {\it znode} que armazena as informações dos servidores,
é disparado pelo ZooKeeper, significa que o recurso está indisponível e o módulo
deve deixar esse recurso em modo de espera para ser apagado da estrutura do
servidor ZooKeeper, até que os demais módulos tenham feito a recuperação de
seus dados contidos no ZooKeeper referente a este servidor.
As mudanças nos serviços relativos à sua forma de funcionamento e integração ao
ZooKeeper, estão descritas de forma mais detalhada nas próximas seções.

\subsection{\it Discovery Service}
\label{subsec:discovery}
Neste módulo houve a substituição das mensagens \textit{broadcast} da rede P2P
pelo controle feito via ZooKeeper. Assim sendo, não há mais a necessidade de
pesquisar na rede P2P formada pelo BioNimbus para a descoberta da existência e
características dos servidores, pois esses dados são lançados no servidor
ZooKeeper assim que o novo servidor inicia sua execução no ZooNimbus. O novo
\textit{znode} criado para o novo servidor e os seus \textit{znodes}
filhos contém as informações que são utilizadas pelo
ZooNimbus para gerenciar a federação e para que os módulos
funcionem de forma correta.
Dessa forma, após o lançamento dos dados, o serviço de descoberta executa uma rotina de
reconhecimento dos mesmos dados lançados por outros servidores que já
iniciaram a execução do ZooNimbus, conhecendo as informações dos demais
servidores.
Assim, sempre que for necessário verificar quais servidores estão
disponíveis podem ser realizadas leituras de seus dados no ZooKeeper, além da
utilização de \textit{watchers} para notificar as mudanças nos
servidores.

Com a utilização desses \textit{watchers} não há a necessidade do serviço de
descoberta realizar constantes verificações de indisponibilidade dos
servidores, provendo assim uma maior consistência, já que os
servidores contidos no ZooNimbus não irão ficar
indisponíveis sem que os demais servidores sejam avisados. A utilização
da classe  \textit{PluginInfo} se manteve, classe na qual é abstraido os
dados de cada servidor, dados que são armazenados no \textit{znode}
\textit{peer\_IdPlugin} do ZooKeeper, podendo ser facilmente acessados por
qualquer outro servidor.

O \textit{znode} \textit{STATUS}, mostrado na
Figura~\ref{fig:zookeeper_storage}, representa a disponibilidade do servidor.
Ele é verificado sempre que necessário, e existirá até que o servidor fique indisponível.
Ao ficar indisponível, o \textit{znode} {\it STATUS} é automaticamente apagado
pelo próprio ZooKeeper. Assim, é enviado um alerta para todos os servidores, e
um novo \textit{znode}, chamado \textit{STATUSWAITING} é criado, permitindo aos
demais serviços iniciem a recuperação das informações necessárias contidas nos
\textit{znodes} daquele servidor antes que sua estrutura seja apagada pelo
módulo de monitoramento, o \textit{Monitoring Service}. Desta forma, mantém-se
um controle consistente da tolerância a falha, pois antes de iniciar a rotina de
recuperação dos dados, é verificado se já existe o \textit{znode} 
\textit{STATUSWAITING} e se a recuperação já foi realizada por outro servidor.
Todos os módulos realizam e solicitam notificações de indisponibilidade de todos
os servidores da federação.
Para isso, um \textit{watcher} é criado no \textit{znode} de cada servidor do
ZooNimbus.

Esta nova implementação de descoberta da rede que forma a federação em nuvem,
continua permitindo a escalabilidade do sistema, a utilização de recursos
estáticos e dinâmicos, e a flexibilidade com os diversos tipos de recursos.

\subsection{\it Monitoring Service}
\label{subsec:monitoring}

O atual módulo de monitoramento modifica em partes sua proposta inicial de
pedido de execução e acompanhamento do estado das tarefas que 
foram enviadas para execução. Na nova implementação, o seu principal
acompanhamento é referente aos servidores que formam a federação em nuvem. Esse
acompanhamento é feito de forma integrada ao ZooKeeper, com a utilização de
\textit{watchers} e tratamento de alertas dos mesmos, realizando
assim um trabalho de monitoramento livre de envio e solicitação
de mensagem via rede P2P. Isso evita consumo de banda e possíveis erros com o não
recebimento de dados. Agora, as solicitações e o envio de dados são todos
direcionados ao servidor ZooKeeper.

O módulo de monitoramento permite ainda que os principais dados utilizados pelos
módulos de cada servidor, armazenados na estrutura do ZooKeeper, possam
ser recuperados para possíveis reconstruções ou garantias de execuções de
serviços solicitados ao ZooNimbus, como foi brevemente descrito na seção
anterior.

Dessa forma, quando este módulo recebe o aviso do ZooKeeper de que o \textit{znode} 
\textit{STATUS} foi excluído, ele verifica se  o \textit{znode} 
\textit{STATUSWAITING} existe e se não existir, realiza a sua criação,
permitindo aos demais módulos a recuperação dos dados desse servidor.
Como não é possível garantir a ordem cronológica do envio dos avisos realizados
pelo ZooKeeper, todos os demais módulos que solicitaram o aviso de
indisponibilidade dos servidores,  podem realizar a criação do
\textit{znode}  \textit{STATUSWAITING} ao receberem a notificação de
indisponibilidade para iniciarem suas rotinas de recuperação dos dados.
Ao checar a existência do \textit{znode} \textit{STATUSWAITING}, o
\textit{Monitoring Service} irá verificar se todos os demais módulos já
realizaram a recuperação dos dados por meio da análise das informações contidas
em forma de \textit{string} no \textit{STATUSWAITING}. Caso seja verificado que
todos os módulos já tenham realizado a recuperação, a estrutura referente ao
\textit{peer\_IdPlugin} indisponível será apagada.
E caso seja verificado  que os módulos ainda não realizaram suas recuperações de
dados, o módulo de monitoramento irá aguardar até que seja feita a recuperação, para
então excluir o \textit{znode}.

\subsection{\it Scheduling Service}
\label{subsec:scheduling}

A forma de realizar o serviço de escalonamento foi completamente refeita, pois
foi baseada nas novas tecnologias utilizadas, ZooKeeper
Apache~\cite{zookeeper2010} e Avro Apache ~\cite{avro2012}. A comunicação entre
servidores para verificar o estado das tarefas em execução ou para
escalonamento é agora feita com a utilização do ZooKeeper, deixando de ser
direcionada servidor a servidor. Os procedimentos são realizados quando é
necessário informar o estado, a atualização e a inclusão das tarefas. Esses
dados são enviados e recuperados diretamente do servidor ZooKeeper.

Quando um cliente está conectado a um servidor ZooNimbus, ou seja, conectado a
qualquer servidor que participe da federação em nuvem, ele solicita a
execução de uma tarefa, o servidor será o responsável por receber essa
solicitação e lançá-la no \textit{znode} \textit{jobs} do servidor ZooKeeper,
juntamente com todas as informações referentes ao \textit{job}, informações que
são necessárias para realizar a execução do \textit{job}. Ao realizar esse
lançamento, o módulo de escalonamento que está constantemente rodando e
aguardando uma notificação é alertado sobre o novo \textit{job} a ser executado. 

O método responsável pelo tratamento de notificações do módulo de escalonamento
irá recuperar todas as informações lançadas no ZooKeeper e encaminhá-las para
que a rotina de escalonamento seja realizada. 

Para que esse novo modelo de implementação do módulo de escalonamento
possa ser bem utilizado pelas políticas de escalonamento, elas devem implementar
o método visualizado na Figura~\ref{fig:metodo} que retorna um mapa das
tarefas, representado por \linebreak{\it Map<JobInfo,PluginInfo>} na assinatura
da Figura~\ref{fig:metodo}, enviadas para o escalonamento, com o servidor eleito,
representado pela variavél {\it PluginInfo} e que está contida no retorno do
mapa, pelo escalonador para executar a tarefa.
Para a chamada desse método deve ser informado as tarefas à serem escalonadas,
tarefas que estão contidas na coleção {\it Collection<JobInfo> jobinfos}, e
também a conexão utilizada com o Zookeeper,a qual a variavél {\it zk} está
representando.

 \begin{figure}[H]
					\centering
					\includegraphics[width=13cm, height=2cm]{imagens/metodo.jpg}\\
					\caption{Assinatura que Deve ser Implementada ao Utilizar a Interface {\it
					SchedPolicy}.}
					\label{fig:metodo}
\end{figure}

Após a realização da rotina e da política de escalonamento, um servidor
é eleito para executar o \textit{job}, e essa informação é disponibilizada por
meio do ZooKeeper. Um novo \textit{znode} \textit{task\_IdJob}, contendo as
informações da tarefa, é criado dentro do \textit{znode}
\textit{task} do servidor eleito para o escalonamento e o \textit{znode}  
\textit{job\_IdJob}, que pertencia ao \textit{znode} \textit{jobs},{\it znode}
que armazena as requisições de execução de tarefas, é apagado.
Essa criação de um novo \textit{znode} gera o disparo do \textit{watcher}
adicionado no \textit{znode} \textit{tasks} pelo módulo de escalonamento que
irá tratar esse alerta como um pedido de execução da tarefa.
O método \textit{event()}, mostrado na Tabela \ref{tab:interfaceZooNimbus}, irá realizar o
tratamento do alerta recebido e encaminhará o pedido de execução da tarefa.
Assim, quando a execução é finalizada, o método que realizou sua execução irá modificar
o estado da tarefa no ZooKeeper, de tarefa "Executando" para
"Executada". Ao perceber a mudança, o ZooKeeper novamente irá informar
ao \textit{Scheduling Service} por meio de um \textit{watcher} do novo estado da
tarefa e a rotina de conclusão da tarefa irá ser iniciada após esse alerta.

As tarefas de bioinformática executadas no ambiente da federação necessitam de
arquivos de entrada que contêm os dados que serão processados pelos serviços de
bioinformática, e geram um ou mais arquivos de saída.
Esses arquivos devem existir em algum recurso da federação antes de ser
solicitada a execução do serviço. Caso esse arquivo não exista em nenhum
servidor, o ZooNimbus informa ao cliente que solicitou a execução, e que os
arquivos de entrada devem ser disponibilizados na plataforma. Assim, o cliente
deve então realizar o \textit{upload} do arquivo, serviço provido de forma
transparente pelo módulo de armazenamento. O módulo de armazenamento irá então
executar uma política que elege para qual servidor o arquivo de entrada será
enviado. 

Considerando que o módulo de armazenamento poderá enviar o
arquivo de entrada para um servidor diferente daquele que o módulo de
escalonamento solicitar a execução do serviço, ambos os módulos devem
realizar algum procedimento que minimize esse conflito que pode aumentar o
tempo de execução de uma tarefa. O procedimento realizado para esse cenário 
consiste na utilização de um parâmetro comum, que é utilizado por ambos os
módulos para eleger um servidor, a latência, que é calculada entre o
local que contém o arquivo para cada um dos demais recursos da federação. A
latência é utilizada pela política de escalonamento ao calcular o poder
computacional de um servidor, podendo alterar o valor da probabilidade de sua
escolha do recurso.

Ao finalizar a execução da tarefa, o usuário poderá então realizar o
\textit{download} do arquivo de saída independente da localidade deste arquivo.

\subsection{\it Fault Tolerance Service}
\label{subsec:faulttolerance}

Na nova implementação, este serviço está presente em todos os demais serviços, o
que favorece o seu desempenho e garante um maior sucesso na instalação da
tolerância a falhas.

No \textit{Storage Service}, o {\it Fault Tolerance} está presente
quando recebe o alerta sobre a indisponibilidade de um servidor e cria um
\textit{znode} \textit{STATUSWAITING}, sinalizando essa indisponibilidade para os
demais módulos, e realizando a rotina de recuperação dos arquivos que continham
nesse servidor. Como os arquivos são armazenados de forma duplicada nos
servidores, o módulo de armazenamento irá identificar qual o
servidor que contém esses arquivos e irá realizar a duplicação dos
mesmos em outro recurso da federação em nuvem.

No \textit{Scheduling Service}, o {\it Fault Tolerance} está presente quando ele
também recebe o alerta informando sobre a indisponibilidade do servidor
e inicia a recuperação de todas as tarefas que foram escalonadas para lá,
e ainda não foram executadas ou estavam em execução, de acordo
com os meta-dados armazenados no ZooKeeper, realocando-os para o \textit{znode} 
\textit{jobs}, onde irão aguardar um novo escalonamento.

No \textit{Monitoring Service} a tolerância a falha ocorre quando recebe o
alerta que informa sobre a indisponibilidade do servidor, e inicia o
procedimento para verificar se os demais módulos já realizaram a recuperação de
todos os dados armazenados no servidor \textit{ZooKeeper}. O \textit{Monitoring
Service} também é responsável pela verificação do não escalonamento de algum
\textit{job} ou não execução de alguma tarefa já escalonada, assim como pela verificação
da inclusão de algum arquivo nos recursos que o módulo de armazenamento não
tenha reconhecido.

Toda essa recuperação é possível porque os recursos da federação se conhecem
e mantêm um \textit{watcher} nos seus respectivos \textit{znodes STATUS}.
As alterações citadas nas seções anteriores mostraram as mudanças que ocorreram
no ZooNimbus. Assim, na próxima seção será explicado detalhadamente todo o processo que
o {\it Storage Service} passou e quais as mudanças que ocorreram com relação ao tratamento
de arquivos na federação em nuvens.

\section{\it Storage Service}
\label{sec:politica}
O ZooNimbus tem sido usado para executar, não de forma exclusiva,
aplicações de bioinformática que necessitam de grande poder de processamento e
armazenamento.
Essas aplicações de um modo geral possuem características semelhantes, as principais são:
\begin{itemize}
  \item Um conjunto de arquivos de entrada que serão analisados pela ferramenta, que
podem ser originados a partir da saída da máquina de sequenciamento ou de outra
ferramenta de bioinformática;
\item Um conjunto de arquivos de saída contendo o resultado da análise realizada pela
ferramenta;
\item Um conjunto de parâmetros de execução que podem influenciar, por exemplo,
na utilização dos recursos de hardware ou no formato dos arquivos de entrada e de
saída.
\end{itemize}
Assim, a arquitetura prevê uma série de estudo de caso que tentam se adequar
às características listadas. O objetivo é prover um conjunto completo de
ações que possam ser realizadas sobre a arquitetura ZooNimbus, de forma que os
serviços de interação com o usuário sejam capazes de atender às necessidades dos
pesquisadores~\cite{hugo2012}.

O {\it Storage Service} passou por várias modificações, anteriormente, quando um
{\it upload} era feito na federação, o servidor no qual o cliente estava
conectado recebia uma solicitação de {\it upload} e mandava o arquivo para o
primeiro servidor que estivesse disponível, sem realizar nenhuma análise que
pudesse escolher o servidor mais adequado para armazenar o arquivo solicitado.

Assim, serão descritas, nas próximas seções, as formas de execução dos
principais serviços da política de armazenamento que foram implementadas no {\it
Storage Service}, as quais são: {\it upload}, replicação, {\it download} e
tolerância a falhas.
Também será descrito a forma que a ZooClouS coleta os dados dos servidores para
o cálculo do custo de armazenamento, e a maneira de como é feito este cálculo.

\subsection{{\it Upload} de Arquivos}
\label{subsec:upload}
Para executar ferramentas de bioinformática no ZooNimbus é necessário que os
arquivos de entrada utilizados pelas ferramentas estejam dentro da federação. Assim, um
{\it upload} é necessário também, caso o arquivo de saída gerado seja armazenado
em uma infraestrutura diferente da que realizou o processamento.
Logo, conforme mostra a Figura~\ref{fig:upload}, o {\it upload} de arquivos é
realizado por meio dos seguintes passos:

\begin{figure}[H]
    \centering
	\includegraphics[width=14cm, height=8cm]{imagens/casodeuso-upload.jpeg}\\
	\caption{Sequência de Ações que Ocorrem Durante o Processo de {\it Upload}}
	\label{fig:upload}
\end{figure}

\begin{enumerate}

  \item O cliente realiza uma requisição de {\it upload} por meio do console,
  passando o caminho do arquivo de entrada na máquina onde ele está se
  conectando ao ZooNimbus.

  \item O servidor ZooNimbus em que ele está conectado consulta as informações
  do arquivo e verifica quais servidores dentro da federação possuem espaço livre
  suficiente para receber o arquivo, sendo que cada servidor utiliza no máximo
  90\% de sua carga de armazenamento. Este valor foi definido pela necessidade
  de espaço em disco no servidor, para que este possa iniciar o sistema
  operacional e também para que pastas que geram arquivos temporários tenham
  espaço em disco para a criação destes arquivos. Esse valor poder ser
  facilmente alterado, bastando apenas modificar a variável {\it MAXCAPACITY}
  presente no {\it Storage Service}.
  
  \item O servidor retorna para o cliente uma lista com todos os servidores
  disponíveis para o armazenamento. 

  \item No cliente é realizado o cálculo da latência entre ele e cada servidor
  disponível na federação, logo após ele devolve a lista com a latência de cada
  servidor em relação ao cliente para o servidor.

  \item O servidor recebe a lista e envia para o {\it Storage Service}, onde
  será feito o cálculo da política de armazenamento de cada servidor, este
  cálculo será detalhado na Seção~\ref{subsec:custo_armazenamento}. 
  
  \item O servidor irá criar uma entrada no {\it znode} chamado {\it
  pending saves}, com os dados do arquivo que será colocado na federação. Esta
  entrada informa a todos os servidores que um arquivo será enviado para a
  federação e que este envio ainda não foi finalizado, ou seja, está pendente a
  sua conclusão.
  
  \item Com o cálculo feito, o servidor retorna para o cliente uma lista
  ordenada com todos os servidores disponíveis de acordo com o custo de
  armazenamento de cada um (o primeiro servidor possui o menor custo, o segundo
  servidor, o segundo menor custo, e assim por diante).

  \item Após receber a lista, o cliente então realiza a transferência do arquivo
  via {\it SSH File Transfer Protocol - SFTP} para o melhor servidor. Em
  caso de erro durante o envio do arquivo, falha na rede ou qualquer outro
  problema, o envio do arquivo será automaticamente direcionado para o próximo
  servidor da lista.
  
  \item O servidor irá criar uma entrada no {\it znode} do servidor onde o
  arquivo foi armazenado com os seus dados (nome, ID, tamanho, etc). 
  
  \item Em seguida, o cliente recebe uma mensagem confirmando que seu
  arquivo foi enviado com sucesso.

\end{enumerate}

Quando for finalizado o {\it upload}, será possível ver se o arquivo está no
ZooNimbus por meio do comando {\it files}, o qual lista todos os arquivos
contidos em todos os servidores da federação. Depois que o cliente recebe uma
confirmação de que o arquivo foi colocado na federação com sucesso, o servidor
que recebeu o arquivo irá iniciar a rotina de replicação, descrita na
Subseção~\ref{subsec:replicacao}.

\subsection{Replicação de Arquivos}
\label{subsec:replicacao}
Toda vez que um arquivo é colocado dentro do ZooNimbus, o servidor que recebeu o
arquivo irá iniciar o processo de replicação deste arquivo em outros servidores na
federação de nuvens de acordo com o fator de replicação definido, por padrão,
este valor foi definido em 2. Logo, isto faz com que no mínimo uma cópia do arquivo
fique disponível em outro servidor.
Conforme a Figura~\ref{fig:replicacao}, o processo de replicação segue os passos abaixo:

\begin{figure}[H]
    \centering
    \includegraphics[width=14cm, height=6cm]{imagens/casodeuso-replicacao.jpeg}\\
	\caption{Sequência de Ações que Ocorrem Durante o Processo de Replicação dos Arquivos.}
	\label{fig:replicacao}
\end{figure}

\begin{enumerate}
  
  \item O {\it upload} é concluído no melhor servidor de acordo com o cálculo
  do custo de armazenamento feito pela política de armazenamento.
   
  \item O servidor que recebeu o arquivo consulta uma lista com todos os
  servidores que possuem espaço disponível para armazenar o arquivo.
  
  \item De acordo com o número de cópias definidas, no caso em estudo é dois, o
  ZooNimbus consulta os servidores disponíveis e copia o arquivo para o
  melhor servidor em relação ao mesmo que possue o arquivo, através do protocolo
  SFTP~\cite{RFC4253} até que a operação seja concluída com sucesso.
  
  \item Para cada servidor que recebe a cópia do arquivo, é criada uma entrada
  em seu respectivo {\it znode} com os dados do arquivo que foi copiado.
  
  \item Com o número de cópias alcançado, o servidor interrompe a rotina e termina a replicação.
\end{enumerate}

Esta sequência de ações ocorre após um envio de um arquivo na federação. Existe
outra situação que também utiliza o processo de replicação, que é a tolerância a
falhas, que será descrita na Subseção~\ref{subsec:tolerancia}.

\subsection{Tolerância a Falhas}
\label{subsec:tolerancia}
Para garantir que um arquivo possua uma quantidade mínima de cópias dentro da
federação, foi criada uma rotina de tolerância a falhas. A
Figura~\ref{fig:tolerancia} mostra a sequência de ações durante o processo de
tolerância a falhas.

\begin{figure}[H]
    \centering
    \includegraphics[width=14cm, height=8cm]{imagens/casodeuso-tolerancia.jpeg}\\
    \caption{Sequência de Ações que Ocorrem Durante o Processo de Tolerância a Falhas.}
	\label{fig:tolerancia}
\end{figure}

\begin{enumerate}
  	\item Quando um servidor cair ou se desconectar da federação. Como cada
  	servidor possui uma quantidade {\it x} de arquivos, esses arquivos são
  	replicados para outros servidores garantindo o número mínimo de cópias
  	definido, por meio do Zookeeper ocorre o alerta sobre a situação do servidor.
	
	\item Os recursos, assim como arquivos do servidor tornam-se inacessíveis
	para os outros servidores. Assim sendo, para garantir que todos os arquivos que
	ficaram inacessíveis tenham a quantidade mínima de cópias, a tolerância a falhas
	irá verificar quais arquivos ficaram inacessíveis. 
	
	\item Após isso, o ZooNimbus irá contactar via RPC o servidor que possuir uma
	cópia do arquivo que está inacessível avisando que ele deve replicar aquele
	arquivo para algum servidor. 
	
	\item O servidor que tiver o arquivo irá rodar o cálculo do custo de
	armazenamento entre ele e os servidores da federação, e irá replicar para o que
	tiver o menor custo de armazenamento para ele. 
	
	\item A replicação é concluída.
	
	\item Por último, os dados do arquivo são atualizados no ZooKeeper de cada
	servidor que tiver o arquivo e o {\it znode} do servidor que se desconectou é deletado.
\end{enumerate}
 

A tolerância a falhas garante que nenhum arquivo dentro da federação tenha um número de
cópias menor do que {\it REPLICATIONFACTOR}. Com isto, a disponibilidade de um arquivo
aumenta. Assim, se um servidor se desconectar e ao mesmo tempo uma requisição para
um {\it download} dos arquivos que se tornaram inacessíveis seja feita,
o ZooNimbus irá buscar a cópia deste arquivo em outro servidor
que tenha o arquivo, enquanto isso outro servidor iniciará o tratamento dos
arquivos que não estão mais disponíveis.

\subsection{{\it Download} de arquivos}
\label{subsec:download}
No ZooNimbus existem duas situações diferentes de requisições de {\it download}. Na
primeira, o cliente/usuário deseja baixar um arquivo que é o resultado de
algum processamento ou que apenas está armazenado na federação. O segundo caso é
quando um cliente/servidor precisa de determinado arquivo dentro da federação
para que ele seja utilizado como entrada para a execução de outro processamento.
O {\it download} de arquivos pelo cliente pode ser descrito nos seguintes
passos, demonstrados na Figura~\ref{fig:download}:

\begin{figure}[H]
    \centering
    \includegraphics[width=14cm, height=6cm]{imagens/casodeusodownload.jpeg}\\
	\caption{Sequência de Ações que Ocorrem Durante o Processo de {\it Download}.}
	\label{fig:download}
\end{figure}

\begin{enumerate}
\item O cliente solicita um arquivo para a federação enviando o seu nome para o
servidor ZooNimbus em que está conectado.
\item Este servidor, via Avro~\cite{avro2012} faz uma chamada RPC para recuperar uma
lista com todos os servidores da federação, e seus respectivos arquivos que estão
armazenados nele.
\item A lista com todos os servidores e arquivos é recebida pelo servidor. O
{\it Storage Service} realiza uma rotina para percorrer todos os arquivos e ir
comparando com o nome do arquivo solicitado.
\item Se o arquivo é encontrado, o servidor ZooNimbus retorna o
endereço IP do servidor que possui o arquivo solicitado.
\item Após receber o IP, o cliente abre uma conexão SFTP com o servidor
passando o arquivo que deseja como parâmetro. A conexão é estabelecida e o
o {\it download} do arquivo é realizado.
\end{enumerate}

A sequência de ações mostrada na Figura~\ref{fig:download}, descreve
situação em que o cliente/usuário solicita o arquivo. No caso de um
cliente/servidor precisar de um arquivo, as ações são as seguintes:

\begin{enumerate}
\item Um serviço de um cliente/servidor precisa de um arquivo para realizar um
processamento, esse serviço consulta no ZooKeeper onde tal arquivo está
localizado, retornando o {\it ip} do servidor onde está situado o arquivo.
\item Com o {\it ip} do servidor que possui o arquivo, uma conexão é
feita entre o cliente/servidor e o servidor que possui o arquivo, e
o arquivo é transferido.
\item Após o arquivo ser transferido para o cliente/servidor, para o mesmo é
criado um {\it znode} no respectivo cliente/servidor.
\end{enumerate}

A diferença entre as duas chamadas está no fato do cliente/servidor não precisar
fazer requisição de um arquivo para o {\it Storage Service}, ele realiza a
operação apenas descobrindo o {\it ip} do servidor que possui o arquivo.

\subsection{Listagem de Arquivos}
\label{subsec:listagem}
A listagem de arquivos é necessária para se obter todos os arquivos contidos na
federação, não importando em qual servidor o usuário está conectado.  A
sequência de passos é mostrada na Figura~\ref{fig:listagem}.

\begin{figure}[H]
    \centering
    \includegraphics[width=14cm, height=5cm]{imagens/casodeusolistagem.jpeg}\\
    \caption{Sequência de Ações que Ocorrem Durante o Processo de Listagem dos Arquivos.}
	\label{fig:listagem}
\end{figure}

\begin{enumerate}
  \item O cliente realiza uma requisição de listagem de arquivos. 
  \item O servidor o qual o cliente está conectado passa o comando para o {\it
  Storage Service}. 
  \item O {\it Storage Service} irá realizar uma consulta no ZooKeeper para
  obter os arquivos de cada servidor da federação.
  \item O ZoooKeeper retorna a lista de todas as entradas de arquivos presenetes
  no mesmo.
  \item O {\it Storage Service}, realiza um tratamento na lista retornada,
  enviando uma lista contendo nomes únicos dos arquivos presentes na federação.
\end{enumerate}

Os nomes dos arquivos são mostrados em forma de uma única lista, dando a ilusão
de que o usuário está conectado em apenas um computador, e que este único
computador possui todos os arquivos mostrados. Todos os arquivos contidos na
federação possuem ao menos uma cópia, porém quando o comando de listagem é
executado, os arquivos são mostrados apenas uma vez, escondendo do usuário a
quantidade de cópias existentes dos arquivos encontrados. Caso o usuário queira
baixar um dos arquivos na lista, basta apenas digitar o comando {\it download}
seguido do nome do arquivo que deseja transferir para sua máquina.
O armazenamento de arquivos em um ambiente de federação de nuvens recebe várias
demandas de várias fontes diferentes. Neste cenário, é necessário um algoritmo
para realizar a escolha do melhor servidor para se armazenar o arquivo enviado
para o ZooNimbus. 

Na Subseção~\ref{subsec:algoritmoA} será explicado o Algoritmo
A*, no qual foi utilizado a sua base para a implementação do algoritmo proposto na
Subseção~\ref{subsec:custo_armazenamento}, o ZooClouS.

\subsection{O Algoritmo A*}
\label{subsec:algoritmoA}

O Algoritmo A* [42] é um algoritmo de busca de melhor caminho, muito utilizado
em jogos, cuja função é buscar o melhor caminho entre um ponto inicial e um
ponto final. Para um ambiente em nuvens foi implementado por Ren
Xun-Yi~\cite{xun2010}, um algoritmo baseado no A*, com o intuito de otimizar o
custo de armazenamento.
Segundo Ren Xun-Yi~\cite{xun2010}, dado um ambiente de armazenamento em nuvens,
{\it N} usuários necessitam armazenar D = (D1, D2, \ldots, Dn) dados no
ambiente, que possui {\it M} nós CM = (C1, C2, \ldots, CM). Para um usuário, o
custo de armazenamento de um dado $D_i$ em um nó $C_j$ inclui duas partes: é o
custo do tempo necessário para transferir o dado {\it D} para o nó $C_j$, e a
outra parte é o preço de armazenamento de $C_j$. A fórmula desta situação é
mostrada na Equação~\ref{equation:aparte1}, onde {\it trans} é a função que
calcula o custo do tempo de transferência do arquivo, passando como parâmetro
os dados do arquivo, representado pela variável {\it D}, os dados do cliente
representado pela variável {\it user} e as informações do nó destino
representado por {\it $C_j$} e {\it s} é a função que calcula o preço do
armazenamento do arquivo {\it D} no nó {\it $C_j$}:

\begin{equation}
\label{equation:aparte1}
StorageCosts = trans(D, user, C_j) + s(D, C_j)
\end{equation}

O custo total de acessos de {\it N} usuários é a transmissão de dados dos nós
da nuvem para todos os usuário, conforme mostrada na Equação~\ref{equation:aparte2}.

\begin{equation}
\label{equation:aparte2}
AccesCosts = \sum_{i=1}^{N}trans(D, C_j, user_i)
\end{equation}

O objetivo da otimização é que o custo total de armazenamento e de acesso sejam
mínimos, conforme é mostrado na Equação~\ref{equation:aparte3}:

\begin{equation}
\label{equation:aparte3}
TotalC = Min(\sum_{k=1}^{M} \alpha * {\it AccessCost_k} + \beta * {
Storagecosts_k})
\end{equation}

Onde $\alpha$ e $\beta$ são fatores de peso, e $\alpha$ + $\beta$ = 1. A restrição do
problema de otimização de armazenamento é indicada na Equação~\ref{equation:aparte4}

\begin{equation}
\label{equation:aparte4}
AvailStorage \ge SizeofReqData
\end{equation}

Um fator simples que deve ser considerado é que nuvens respondam de forma
eficiente o acesso do usuário. A otimização do A* utiliza como possível solução
do custo de armazenamento a Equação~\ref{equation:aparte5}:

\begin{equation}
\label{equation:aparte5}
EV(s) = D(s) + V(s)
\end{equation}

D(s) é o custo pago, e V(s) é o custo potencial. Assumindo que a solução ótima é
V(s)*, então o atual custo é mostrado na Equação~\ref{equation:aparte6}:

\begin{equation}
\label{equation:aparte6}
EV(s) = D(s) + V(s)*
\end{equation}

Uma vez que V(s)* é desconhecido, usa-se o menor {\it Limite superior} de V(s):
Ls(V(s)) em vez de V(s)*, dado pela Equação~\ref{equation:aparte7}:

\begin{equation}
\label{equation:aparte7}
Ls(V(s)) \leq V(s)*
\end{equation}

Em seguida, $D(s) +  Ls(V(s)) < D(s) + V(s)*$. Usando $D(s) + V(s)*$ para
calcular a atual solução proposta, independentemente de D(s) ser bom ou ruim,
uma solução não é perdida com isto. Para o ambiente de nuvens, o A* é combinado
com o algoritmo Min-Min~\cite{minmin} para procurar a melhor solução. A
descrição do algoritmo A* é mostrada na Figura~\ref{fig:algoritmoa}.

\begin{figure}[H]
    \centering
    \includegraphics[width=10cm, height=9cm]{imagens/algoritmoa.png}\\
    \caption{Algoritmo A* Otimizado para Armazenamento em Nuvens}
	\label{fig:algoritmoa}
\end{figure}

O algoritmo A* tem um efeito de otimização baixo se um algoritmo de escolha ao
acaso for utilizado para uma solução inicial. Por conta disto, na linha 1 do
algoritmo é utilizado o algoritmo Min-Min~\cite{minmin} para gerar uma melhor solução
aproximada. O algoritmo Min-Min distribui dados para um nó na nuvem com um custo
de armazanamento mínimo, um de cada vez, em seguida, remove o nó ocupado da
lista. Ocorre uma consideração apenas de distribuições ideais no algoritmo Min-Min, pois uma
remoção cega de um nó de armazenamento pode apagar uma solução ideal e, portanto, não
alcançar o melhor custo global entre as nuvens. 

Para evitar esse problema, a
altura da árvore é obtida na segunda linha e, em seguida, é gerada uma árvore
inicial, onde qualquer dado {\it N} pode ser armazenado em um nó {\it M} na nuvem, gerando a
alocação {\it N} * {\it M}, como resultado, a árvore inicial tem {\it N} camadas e cada camada tem
{\it M} ramos. Com isso, uma remoção cega é evitada. No {\it loop while} (linhas 4 a
14 da Figura~\ref{fig:algoritmoa}), uma viagem na árvore é realizada. Em primeiro lugar, é verificado se o
custo total de armazenamento de um nó atende a Equação~\ref{equation:aparte3}, depois utiliza-se a
Equação~\ref{equation:aparte4} para calcular o custo total. Se o atual custo calculado for de um nó
folha, {\it Bestcost} é atualizado, se $CurrentCost < BestCost$, {\it
BestCost} é substituido por {\it CurrentCost}, através deste, o algoritmo
mantém a solução ideal nas linhas 5 e 6 da Figura~\ref{fig:algoritmoa}. Custo V(S) é calculado se o nó atual está
na camada do meio, este cálculo é feito a partir do nó raiz para o nó atual e,
em seguida, comparar o provável custo pago com {\it BestCost} para determinar se
deve ou não podar a árvore. Portanto, o algoritmo deve determinar a escolha da
solução calculada somente com o custo minimo do nível seguinte, ele não faz a partir
do nó raiz para os nós folha, com isso, consegue-se o efeito da poda rápida da
árvore.

Neste cenário, um algoritmo de armazenamento foi proposto neste trabalho para o ZooNimbus,
chamado ZooClouS, o qual será apresentado na
Seção~\ref{subsec:custo_armazenamento}.

\subsection{ZooNimbus Cloud Storage - ZooClouS}
\label{subsec:custo_armazenamento}
Quando um arquivo é enviado para a federação de nuvens, um custo de
armazenamento é calculado para aquele arquivo. Este custo é feito
transparentemente entre o cliente que requisitou o {\it upload} e cada servidor que
possui espaço em disco suficiente para receber o arquivo. Para o ZooNimbus,
foram utilizadas três variáveis para o cálculo do custo de armazenamento:
\begin{itemize}

	\item {\it Free-Size}: espaço livre em disco do servidor; 
	\item {\it Uptime}: tempo em que o servidor está {\it on-line} no sistema
	ZooNimbus. É o segundo peso mais importante no cálculo do custo de
	armazenamento, visto que um servidor que está há mais tempo no sistema, gera
	uma maior confiabilidade com relação ao fato de que um servidor pode se
	desconectar da rede e consequentemente da federação. Toda vez que um servidor
	é criado, é adicionado um valor em milissegundos no seu {\it znode}, a partir
	da consulta deste valor, torna-se conhecido o tempo em que este servidor está
	{\it on-line} no sistema.
	\item {\it Latency}: latência entre o cliente e o servidor de destino, é o maior
	peso no cálculo do custo.
\end{itemize}

Baseado no algoritmo A* apresentado na Seção~\ref{subsec:algoritmoA}, proposto
por Xun-Yi~\cite{xun2010}, o cálculo do custo de armazenamento pega dados dos
servidores e define em cada um a variável {\it storagecost}, que será o custo
de armazenamento do dado, de acordo com o cliente que realizou a requisição de
envio. Para o próximo cliente, o {\it storagecost} é sobrescrito com um novo
valor, pois o valor antigo foi configurado de acordo com o cliente que enviou o
último arquivo.

Cada cliente pega instância dos servidores para calcular e setar o custo, isto
evita que um custo seja sobrescrito por outro cliente antes de ser utilizado
pelo cliente anterior. O custo de armazenamento utilizado na política é uma
forma aproximada de definir o tempo de transferência entre origem e destino.
Quanto menor o valor do custo, menor será o custo gasto na transferência do
arquivo. As três variáveis utilizadas para o cálculo do custo de armazenamento
formam a seguinte equação:

\begin{equation}
\label{equation:storagecost}
S = ((U + F) * L) + Costs
\end{equation}

Onde S é o custo de armazenamento, U é o tempo que o servidor está {\it
on-line} ({\it uptime}), F é o espaço livre ({\it free size}) em disco, e L é a
latência calculada entre origem e destino.
Além disso, as variáveis utilizadas possuem pesos com o objetivo de priorizar os
campos mais importantes no cálculo, assim tem-se a Equação~\ref{equation:pesos}:

\begin{equation}
\label{equation:pesos}
\alpha + \beta + \gamma = 1.0
\end{equation}

Com base em vários testes feitos, os valores dos pesos que definiram um melhor
custo de armazenamento foram $\alpha$ com um peso de 0.5, associado à latência,
$\beta$ com um peso de 0.2, associado ao {\it freesize}, e o $\gamma$ atribuído
ao {\it uptime} e possui um peso de 0.3, Conforme mostrado na
Equação~\ref{equation:costwithweight}:

\begin{equation}
\label{equation:costwithweight}
S = ((U * \gamma + F * \beta) * (L * \alpha))
\end{equation}

Esse cálculo forma a primeira parte do custo de armazenamento.
Todavia, como o ZooNimbus trabalha com nuvens públicas ou privadas, pode
acontecer de uma nuvem ser integrada à federação e ela cobrar um preço pela
utilização de seu recurso de armazenamento. Por conta disso, é necessário
adicionar ao custo de armazenamento o preço de armazenar um dado em uma nuvem
que faça cobrança. No caso de nuvens que, geralmente, pertencem a uma
organização privada ou instituição pública e que não cobrem valores para a
utilização de seu disco, o preço do seu armazenamento é 0.
Para outros casos, como por exemplo, a Amazon~\cite{amazon2012} ou
Azure~\cite{microsoft2012}, um valor é cobrado por {\it gigabyte} de dado armazenado.
Este valor é definido no arquivo de configuração da nuvem. 

Com o valor do preço por {\it gigabyte} definido, este custo é adicionado à variável
{\it costs}, que somado ao custo de armazenamento calculado daquela nuvem,
da origem a Equação~\ref{equation:final}:

\begin{equation}
\label{equation:final}
S = ((U * \gamma + F * \beta) * (L * \alpha)) + Costs
\end{equation}

Depois de calculado o custo final de cada servidor, uma lista é recebida com
todos os servidores e ordenada de acordo com os seus custos de armazenamento. A
ordenação é feita de forma crescente, ou seja, do servidor que tem o menor
custo até o maior, sendo que quanto menor o custo, melhor será a transferência
de um arquivo para este servidor.
Após devolver esta lista para quem está requisitando um envio de {\it upload}, o
cliente irá tentar enviar o arquivo ao primeiro da lista, caso não consiga,
continuará tentando o envio no próximo servidor até que o arquivo seja enviado.

\section{Considerações Finais}
\label{sec:consideracoes}
Neste capítulo foi apresentado o ZooClouS, a política de armazenamento
implementada no ZooNimbus, que buscou melhorar o seu desempenho fornecendo
serviços que fazem com que a federação em nuvens consiga atender as requisições
feitas por vários usuários ao sistema.
O ZooClouS mudou a forma de tratamento de arquivos no ZooNimbus, pois com as suas
funções que foram implementadas, a busca de arquivos deixou de ser linear,
tornando-se mais flexível e rápida.

No capítulo~\ref{cap:casodeuso} estão os estudos de caso feitos com o ZooClouS no
ZooNimbus, e as conclusões tiradas a partir da coleta de resultados destes testes.

